package be.kuleuven.thesis.status;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import android.media.MediaPlayer;
import android.media.MediaPlayer.OnBufferingUpdateListener;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.AsyncTask;
import be.kuleuven.thesis.databases.AristoMusicManager;
import be.kuleuven.thesis.framework.Song;
import be.kuleuven.thesis.log.Logger;
import be.kuleuven.thesis.media.SongMediaPlayer;

public class Player implements OnCompletionListener, OnBufferingUpdateListener{
	public static final int SCROLL_DOWN = 1;
	
	private static Player instance;
	private List<Song> currentPlaylist = new ArrayList<Song>();
	private Set<StatusListener> listeners = new HashSet<StatusListener>();
	private SongMediaPlayer currentMediaPlayer;
	private SongMediaPlayer idleMediaPlayer;
	private SongMediaPlayer mediaPlayer1;
	private SongMediaPlayer mediaPlayer2;
	private Song currentSong;
	private boolean buffering;
	
	private Player() {
		mediaPlayer1 = new SongMediaPlayer();
		mediaPlayer2 = new SongMediaPlayer();
		currentMediaPlayer = mediaPlayer2;
		idleMediaPlayer = mediaPlayer1;
	}
	
	public static synchronized Player getInstance() {
		if(instance == null)
			return instance = new Player();
		return instance;
	}
	
	public SongMediaPlayer getCurrentMediaPlayer() {
		return currentMediaPlayer;
	}
	
	public SongMediaPlayer getIdleMediaPlayer() {
		return idleMediaPlayer;
	}
	
	public int getCurrentIndex() {
		return currentPlaylist.indexOf(getCurrentSong());
	}
	
	public int getPlaylistSize() {
		return currentPlaylist.size();
	}
	
	public Song getCurrentSong() {
		return currentSong;
	}
	
	public Song getNextSong() {
		if(getCurrentSong() == getLastSong())
			return null;
		if(currentPlaylist.get(getCurrentIndex()+1) == PlaylistGenerator.getInstance().getDummieSong())
			return null;
		return currentPlaylist.get(getCurrentIndex()+1);
	}
	
	public Song getLastSong() {
		if(currentPlaylist.isEmpty())
			return null;
		return currentPlaylist.get(getPlaylistSize()-1);
	}
	
	public List<Song> getCurrentPlaylist() {
		return currentPlaylist;
	}
	
	public boolean isPlaying() {
		if(getCurrentSong() == null)
			return false;
		return getCurrentMediaPlayer().isPlaying();
	}
	public void pause() {
		if(getCurrentMediaPlayer() == null)
			return;
		getCurrentMediaPlayer().pause();
	}
	
	public void resumePlay() {
		if(PlaylistGenerator.getInstance().isGenerating())
			return;
		if(getCurrentSong() == null && !getCurrentPlaylist().isEmpty()) {
			playSongInPlaylist(0);
		} else if(getCurrentSong() != null) {
			getCurrentMediaPlayer().start();
		}
	}
	
	private void resetPlaylist() {
		currentPlaylist.clear();
		currentSong = null;
		getCurrentMediaPlayer().reset();
		getIdleMediaPlayer().reset();
		PlaylistGenerator.getInstance().reset();
	}
	
	public void playSong(Song song) {
		if(song == getCurrentSong())
			return;
		resetPlaylist();
		currentPlaylist.add(song);
		currentSong = song;
		System.out.println("Load media door play song");
		loadMedia(song);
	}
	
	public void playSongInPlaylist(int index) {
		if(index == getCurrentIndex()+1 && getCurrentSong() != null)
			skip();
		else if(index == getCurrentIndex() && getCurrentSong() != null)
			getCurrentMediaPlayer().seekTo(0);
		else if(index == getCurrentIndex()-1)
			goToPrevious();
		else {
			currentSong = currentPlaylist.get(index);
			if(isPlaying())
				getCurrentMediaPlayer().reset();
			loadMedia(getCurrentSong());
			if(getNextSong() != null) {
				loadMedia(getNextSong());
			}
			for(StatusListener listener: listeners)
				listener.onCurrentSongChanged();
			PlaylistGenerator.getInstance().checkGeneration();
		}
	}
	
	public void addSongToCurrentPlaylist(Song song) {
		if(currentPlaylist.isEmpty()) {
			playSong(song);
		}
		else {
			currentPlaylist.add(song);
			for(StatusListener listener: listeners)
				listener.onSongAdded();
			if(getNextSong() == song) {
				loadMedia(song);
			}
		}
	}
	
	public void addGeneratedSong(Song song) {
		Logger.getInstance().log("Gener", "song generated", song);
		addSongToCurrentPlaylist(song);
		for(StatusListener listener: listeners)
			listener.onSongGenerated();
	}
	
	public boolean canGoBack() {
		return getCurrentSong() != null && getCurrentIndex() != 0;
	}
	
	public boolean canSkip() {
		return getCurrentSong() != null;
	}
	
	public void skip() {
		if(!canSkip())
			return;
		if(getNextSong() == null)
			endOfPlaylist();
		else {
			currentSong = getNextSong();
			if(getIdleMediaPlayer().isLoaded()) {
				switchMediaPlayer();
				getCurrentMediaPlayer().start();
				Logger.getInstance().log("Player", "starting song", getCurrentSong());
				if(getNextSong() != null) {
					loadMedia(getNextSong());
				}
			} else {
				pause();
				buffering = true;
			}
			for(StatusListener listener: listeners)
				listener.onCurrentSongChanged();
		}
		PlaylistGenerator.getInstance().checkGeneration();
	}
	
	public void goToPrevious() {
		if(getCurrentIndex() != 0 && getCurrentMediaPlayer().getCurrentPosition() < 10000) {
			if(!canGoBack())
				return;
			currentSong = currentPlaylist.get(getCurrentIndex()-1);
			getCurrentMediaPlayer().reset();
			loadMedia(getCurrentSong());
			for(StatusListener listener: listeners)
				listener.onCurrentSongChanged();
		} else {
			getCurrentMediaPlayer().seekTo(0);
		}
	}
	
	public void endOfPlaylist() {
		Logger.getInstance().log("Playl", "End of Playlist", null);
		currentSong = null;
		buffering = false;
		getCurrentMediaPlayer().pause();
		getCurrentMediaPlayer().setSong(null);
		for(StatusListener listener: listeners)
			listener.onCurrentSongChanged();
	}
	
	public void removeSong(int index) {
		Logger.getInstance().log("Playl", "song removed", currentPlaylist.get(index));
		if(index == getCurrentIndex())
			skip();
		currentPlaylist.remove(index);
		for(StatusListener listener: listeners)
			listener.onSongRemoved();
		PlaylistGenerator.getInstance().checkGeneration();
	}
	
	public void clearPlaylist() {
		getCurrentMediaPlayer().reset();
		resetPlaylist();
		PlaylistGenerator.getInstance().checkGeneration();
		for(StatusListener listener: listeners)
			listener.onPlaylistCleared();
	}
	
	private void loadMedia(Song song) {
		if(getCurrentSong() == song)
			buffering = true;
		if(getCurrentMediaPlayer().getSong() == null) {
			System.out.println("Loading "+song.getTitle()+" in current media player");
			getCurrentMediaPlayer().reset();
			getCurrentMediaPlayer().setOnCompletionListener(this);
			getCurrentMediaPlayer().setSong(song);
			new LoadMediaTask(song).execute(getCurrentMediaPlayer());
		} else {
			System.out.println("Loading "+song.getTitle()+" in idle media player");
			getIdleMediaPlayer().reset();
			getIdleMediaPlayer().setOnCompletionListener(this);
			getIdleMediaPlayer().setSong(song);
			new LoadMediaTask(song).execute(getIdleMediaPlayer());
		}
	}
	
	private class LoadMediaTask extends AsyncTask<SongMediaPlayer, Void, Void> {
		Song song;
		
		protected LoadMediaTask(Song song) {
			this.song = song;
		}
		
		// automatically done on worker thread (separate from UI thread)
		SongMediaPlayer mediaPlayer;
		protected Void doInBackground(SongMediaPlayer ...args) {
			mediaPlayer = args[0];
			if(song != mediaPlayer.getSong()) {
				System.out.println("Nu al out of date!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
				return null;
			}
			String url = AristoMusicManager.getInstance().getUrl(mediaPlayer.getSong());
			System.out.println("url found for " + song.getTitle());
			try {
				if(song != mediaPlayer.getSong()) {
					System.out.println("out of date");
					return null;
				}
				System.out.println("Ik zet de data source voor " + song.getTitle());
				mediaPlayer.setDataSource(url);
				if(song != mediaPlayer.getSong()) {
					System.out.println("out of date 2");
					return null;
				}
				mediaPlayer.prepare();
			} catch (Exception e) {
				e.printStackTrace();
			}
			return null;
		}

		// can use UI thread here
		@Override
		protected void onPostExecute(final Void unused) {
			mediaPlayer.setLoaded(true);
			if(getCurrentSong() == mediaPlayer.getSong()) {
				buffering = false;
				if(getIdleMediaPlayer() == mediaPlayer)
					switchMediaPlayer();
				System.out.println("Ik start nu " + song.getTitle());
				mediaPlayer.start();
				if(getNextSong() != null && getIdleMediaPlayer().getSong() != getNextSong()) {
					System.out.println("Volgende song is nog niet geladen dus ik laad " + getNextSong().getTitle());
					loadMedia(getNextSong());
				}
				for(StatusListener listener: listeners)
					listener.onMediaPlayed();
				Logger.getInstance().log("Player", "starting song", getCurrentSong());
			} else if(getCurrentSong() != getIdleMediaPlayer().getSong() && getCurrentSong() != getCurrentMediaPlayer().getSong()) {
				System.out.println("Er is te snel geskipt");
				getCurrentMediaPlayer().reset();
				getIdleMediaPlayer().reset();
				loadMedia(getCurrentSong());
			}
		}
	}
	
	private void switchMediaPlayer() {
		if(isPlaying())
			getCurrentMediaPlayer().pause();
		SongMediaPlayer player = getCurrentMediaPlayer();
		currentMediaPlayer = getIdleMediaPlayer();
		idleMediaPlayer = player;
	}
	
	public void setMediaToPosition(int position) {
		int playPositionInMilliseconds = (getMediaFileLengthInMilliseconds()/100)*position;
		getCurrentMediaPlayer().seekTo(playPositionInMilliseconds);
	}
	
	public int getCurrentPosition() {
		if(getCurrentSong() == null)
			return 0;
		return getCurrentMediaPlayer().getCurrentPosition();
	}
	
	public int getMediaFileLengthInMilliseconds() {
		if(getCurrentSong() == null)
			return 1000000;
		return getCurrentMediaPlayer().getDuration();
	}
	
	public int getTotalDuration() {
		int duration = 0;
		for(Song s: getCurrentPlaylist())
			duration += s.getDuration();
		return duration;
	}
	
	public boolean isBuffering() {
		return buffering;
	}

	public void registerStatusListener(StatusListener listener) {
		listeners.add(listener);
	}
	
	public void unRegisterListener(StatusListener listener) {
		listeners.remove(listener);
	}

	@Override
	public void onBufferingUpdate(MediaPlayer mp, int percent) {
//		for(StatusListener listener: listeners)
//			listener.onBufferingUpdate(mp, percent);
	}
	
	public int getProgress() {
		return (int) (((float)getCurrentPosition()/getMediaFileLengthInMilliseconds())*100);
	}

	@Override
	public void onCompletion(MediaPlayer arg0) {
		System.out.println("song comlete: "+ Math.abs(getMediaFileLengthInMilliseconds()-getCurrentPosition()));
		if(Math.abs(getMediaFileLengthInMilliseconds()-getCurrentPosition()) < 2000) {
			Logger.getInstance().log("Player","Song completed", getCurrentSong());
			skip();
		} else {
			System.out.println("MOET IK HIER ZIJN???????????????????????????????????????????");
			getCurrentMediaPlayer().setOnCompletionListener(null);
			getIdleMediaPlayer().setOnCompletionListener(null);
//			getCurrentMediaPlayer().reset();
//			buffering = true;
//			System.out.println("Url is niet gevonden ofzo??????!!!!!????");
//			loadMedia(getCurrentSong());
		}
	}
}